package view_server

import (
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/prometheus/prometheus/promql/parser"
	"go.uber.org/zap"
	"strconv"
	"strings"
)

func createMonitorAlertRule(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.MonitorAlertRule
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析新增告警规则配置请求失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	reqObj.UserID = dbUser.ID

	//  校验一下告警规则
	pt := commonPromqlExprCheck(reqObj.Expr)
	if !pt.Success {
		common.ReqBadFailWithMessage(fmt.Sprintf("ql校验失败:%v", pt.Err), c)
		return
	}

	// 把 xxxName 转化成 xxId

	reqObj.FillDefaultData()

	// 下面级联创建
	err = reqObj.CreateOne()
	if err != nil {
		sc.Logger.Error("新增告警规则配置数据库失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	common.OkWithMessage("创建成功", c)

}

type PromQlCheckResult struct {
	Success bool   `json:"success"`
	Err     string `json:"err"`
}

func commonPromqlExprCheck(ql string) (pt PromQlCheckResult) {
	pt.Success = true
	_, err := parser.ParseExpr(ql)
	if err != nil {
		pt.Success = false
		pt.Err = err.Error()
	}
	return
}

func promqlExprCheck(c *gin.Context) {
	//sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	ql := c.DefaultQuery("ql", "")
	pt := commonPromqlExprCheck(ql)
	common.OkWithDetailed(pt, "ok", c)
}

func getMonitorAlertRuleList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "100"))

	offset := 0
	limit := 0
	limit = pageSize
	if currentPage > 1 {
		offset = (currentPage - 1) * limit
	}
	// 这里只能新增 不能去掉，因为是 多种资源共用的query
	searchUserID := c.DefaultQuery("UserID", "")
	searchUserIDInt, _ := strconv.Atoi(searchUserID)
	searchName := c.DefaultQuery("name", "")
	searchNodePath := c.DefaultQuery("nodePath", "")

	// 	数据库中拿到所有的menu列表

	objs, err := models.GetMonitorAlertRuleAll()
	if err != nil {
		sc.Logger.Error("去数据库中拿所有的告警规则配置错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的告警规则配置错误:%v", err.Error()), c)
		return
	}

	allIds := []int{}
	//// 遍历 role 准备menuIds 列表
	for _, obj := range objs {
		obj := obj
		obj.FillFrontAllData()
		// 在这里处理查询
		if searchName != "" && !strings.Contains(obj.Name, searchName) {
			continue
		}

		if searchNodePath != "" && !strings.Contains(obj.NodePath, searchNodePath) {
			continue
		}
		// 在这里处理查询
		if searchUserID != "" && int(obj.UserID) != searchUserIDInt {
			continue
		}
		allIds = append(allIds, int(obj.ID))

		//role.MenuIds = menuIds
	}

	if len(allIds) == 0 {
		common.OkWithDetailed(allIds, "ok", c)
		return
	}
	objs, err = models.GetMonitorAlertRuleByIdsWithLimitOffset(allIds, limit, offset)
	if err != nil {
		sc.Logger.Error("limit-offset去数据库中拿所有的任务错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("limit-offset去数据库中拿所有的脚本模板错误:%v", err.Error()), c)
		return
	}
	for _, obj := range objs {
		obj.FillFrontAllData()

	}

	resp := &ResponseResourceCommon{
		//Total: models.GetMonitorAlertRuleCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(allIds),
		Items: objs,
	}

	common.OkWithDetailed(resp, "ok", c)
}

func getMonitorAlertRuleOne(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)
	dbObj, err := models.GetMonitorAlertRuleById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找任务实例错误", zap.Any("任务实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	//isRelatedWithMe := false

	//userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	//dbUser, err := models.GetUserByUserName(userName)
	//if err != nil {
	//	sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
	//		zap.Error(err),
	//	)
	//	common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
	//	return
	//}

	dbObj.FillFrontAllData()

	common.OkWithData(dbObj, c)
}

// 告警规则属于上游对象，没有其他对象依赖它，所以可以直接删除
func deleteMonitorAlertRule(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")
	sc.Logger.Info("删除告警规则配置", zap.Any("id", id))

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	deleteMonitorAlertRuleOne(c, intVar)
	common.OkWithMessage("删除成功", c)
}

func deleteMonitorAlertRuleOne(c *gin.Context, id int) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	dbObj, err := models.GetMonitorAlertRuleById(id)
	if err != nil {
		sc.Logger.Error("根据id找告警规则配置错误", zap.Any("告警规则配置", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	err = dbObj.DeleteOne()
	if err != nil {
		sc.Logger.Error("根据id删除告警规则配置错误", zap.Any("告警规则配置", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
}

func updateMonitorAlertRule(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.MonitorAlertRule
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析新增告警规则配置请求失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	_, err = models.GetMonitorAlertRuleById(int(reqObj.ID))
	if err != nil {
		sc.Logger.Error("根据id找告警规则配置错误", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	//  校验一下告警规则
	pt := commonPromqlExprCheck(reqObj.Expr)
	if !pt.Success {
		common.ReqBadFailWithMessage(fmt.Sprintf("ql校验失败:%v", pt.Err), c)
		return
	}

	// 把 xxxName 转化成 xxId

	reqObj.FillDefaultData()
	err = reqObj.UpdateOne()
	if err != nil {
		sc.Logger.Error("事务更新错误", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	common.OkWithMessage("更新成功", c)
}

// 这里不需要参数 ：取反就可以

func enableSwitchMonitorAlertRuleOne(c *gin.Context, id int) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	dbObj, err := models.GetMonitorAlertRuleById(id)
	if err != nil {
		sc.Logger.Error("根据id找告警规则配置错误", zap.Any("告警规则配置", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 这里不需要参数 ：取反就可以

	if dbObj.Enable == common.GORM_ENBALE_RES_YES {
		dbObj.Enable = common.GORM_ENBALE_RES_NO
	} else {
		dbObj.Enable = common.GORM_ENBALE_RES_YES
	}

	err = dbObj.UpdateOne()
	if err != nil {
		sc.Logger.Error("开关更新错误", zap.Any("告警规则配置", dbObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

}

func enableSwitchMonitorAlertRule(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	// 校验一下 menu字段
	id := c.Param("id")
	sc.Logger.Info("开关告警规则配置", zap.Any("id", id))

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	enableSwitchMonitorAlertRuleOne(c, intVar)

	common.OkWithMessage("更新成功", c)
}

// 批量操作的
func batchEnableSwitchMonitorAlertRule(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj BatchSwitchRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量开关规则配置请求失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	for _, id := range reqObj.Ids {
		id := id
		enableSwitchMonitorAlertRuleOne(c, id)

	}

	common.OkWithMessage("更新成功", c)
}

func batchDeleteMonitorAlertRule(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	var reqObj BatchSwitchRequest
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析批量删除规则配置请求失败", zap.Any("告警规则配置", reqObj), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 在这里校验字段，是否必填，范围是否正确
	err = validate.Struct(reqObj)
	if err != nil {

		// 这里为什么要判断错误是否是 ValidationErrors
		if errors, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(

				gin.H{
					"翻译前": err.Error(),
					"翻译后": errors.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return

	}

	for _, id := range reqObj.Ids {
		id := id
		deleteMonitorAlertRuleOne(c, id)

	}

	common.OkWithMessage("删除成功", c)
}
